<?php
// +----------------------------------------------------------------------
// | View 视图服务
// +----------------------------------------------------------------------
// | * 视图层
// | * 1.查询所有关联表的字段
// | * 2.处理字段（可显示、可编辑、可排序、可分组、可过滤）、还要通过临时字段、字段权限进行判断，组装出view需要的字段
// | * 3.组装view视图的表格列
// +----------------------------------------------------------------------
// | 错误编码头 231xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\DelayOrderModel;
use common\model\DelayOrderTaskModel;
use common\model\DelayOrderTaskStepCategoryConfirmModel;
use common\model\OriginalPlanNodeDailyReportModel;
use common\model\OriginalPlanNodeDetailModel;
use common\model\OriginalPlanNodeModel;
use common\model\ProjectNodeReportModel;
use common\model\ReviewFeedbackModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use common\model\TaskWorkOrderModel;
use common\model\TaskWorkOrderProblemStepCategoryModel;
use common\model\UserModel;
use Exception;
use support\ErrorCode;
use support\SingletonTrait;
use think\Hook;
use Webman\Stomp\Client;


class OriginPlanNodeService
{
    use SingletonTrait;

    /**
     * 补充原计划数据
     * @param      $data
     * @param bool $async
     * @return array|mixed|string
     * @throws Exception
     */
    public function bathUpdateOriginPlanNodeInfo($data, bool $async = true)
    {
        $projectId = $data['project_id'] ?? 0;
        $startTime = $data['start_time'] ?? strtotime(date('Y-m-d', time()));
        $endTime = $data['end_time'] ?? strtotime(date('Y-m-d 23:59:59', time()));
        $tenantId = $data['tenant_id'] ?? 0;
        // 同步执行
        if (!$async) {
            return $this->fixOriginalPlanNodeData($startTime, $endTime, $projectId, $tenantId);
        }
        $param['start_time'] = $startTime;
        $param['end_time'] = $endTime;
        $projectList = model(OriginalPlanNodeModel::class)
            ->where(['delivery_time' => ['between', [$startTime, $endTime]]])
            ->where(['status' => 1])
            ->group('project_id')
            ->field('project_id,tenant_id')
            ->select();
        if (empty($projectList)) {
            return [];
        }
        // 伪造请求用户
        $adminData = model(UserModel::class)->where(['id' => 210])->find();
        $adminData['is_system'] = 'no';
        $adminData['active'] = true;
        $adminData['id'] = $adminData['union_id'];
        $adminData['channel'] = '';
        // 异步执行
        foreach ($projectList as $projectItem) {
            $param['project_id'] = $projectItem['project_id'];
            $param['tenant_id'] = $projectItem['tenant_id'];
            $adminData['tenant_id'] = $projectItem['tenant_id'];
            $param['xu'] = base64_encode(json_encode($adminData));
            Client::send('fix_original_plan_node_data', build_queue_data_with_xu($param));
        }
        return $projectList;
    }

    /**
     * 填充节点信息
     *
     *
     * ## 1.查询node-list  delivery-time 在今天（0:00：00  23:59:59） 状态为 1（未处理）
     * ## 2.根据node-item类型执行 所有节点的任务列表和实体列表已存在  单环节执行
     * ## 2.1排期
     * ## 2.1.1 标准节点排期（项目制片）
     * ## 交付任务： 通过任务列表找已交付（7）的任务
     * ## 交付实体： 查询实体列表所有环节任务已完成的实体，排除已取消实体
     * ## 同步更新： 任务列表
     * ## 2.1.2 组内排期（环节制片）
     * ## 交付任务： 通过任务列表找已交付（7）的任务
     * ## 交付实体： 查询实体列表所有环节任务已完成的实体，排除已取消实体
     * ## 同步更新： 任务列表
     * ## 2.2需求单
     * ## 交付任务： 通过任务列表找已交付（7）的任务
     * ## 交付实体： 查询交付任务列表已完成的实体，排除已取消实体
     * ## 2.3延期单
     * ## 交付任务： 通过任务列表找已交付（7）的任务
     * ## 交付实体： 查询交付任务列表已完成的实体，排除已取消实体
     * @param $startTime
     * @param $endTime
     * @param $projectId
     * @param $tenantId
     * @return array
     * @throws Exception
     */
    public function fixOriginalPlanNodeData($startTime, $endTime, $projectId, $tenantId): array
    {
        $originPlanNodeList = model(OriginalPlanNodeModel::class)
            ->where(['status' => OriginalPlanNodeModel::STATUS_UN_EXEC, 'project_id' => $projectId])
            ->where(['delivery_time' => ['between', [$startTime, $endTime]]])
            ->select();
        if (empty($originPlanNodeList)) {
            return [];
        }

        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $finalStatus = StatusService::getInstance()->getTaskDoneStatus();

        $originPlanNodeUpdateList = [];
        foreach ($originPlanNodeList as $item) {
            if (empty($item['all_task_ids'])) {
                continue;
            }
            $taskList = $this->getEntityTaskInfo($item['all_task_ids']);
            foreach ($taskList as $taskItem) {
                // 已交付任务
                if ($taskItem['task_status'] == $finalStatus['id']) {
                    $deliveryTaskIds[] = $taskItem['task_id'];
                }
                // 已交付实体
                if ($taskItem['entity_status'] == $finalStatus['id']) {
                    $deliveryAllTaskIds[] = $taskItem['entity_id'];
                }
                // 已取消的任务
                if ($taskItem['task_status'] == $closedStatus['id']) {
                    $deprecatedTaskIds[] = $taskItem['task_id'];
                }
                // 已取消的实体
                if ($taskItem['entity_status'] == $closedStatus['id']) {
                    $deprecatedAllEntityIds[] = $taskItem['entity_id'];
                }
            }
            $originPlanNodeUpdateList[] = [
                'id' => $item['id'],
                'delivery_task_ids' => join(',', $deliveryTaskIds ?? []),
                'delivery_entity_ids' => join(',', $deliveryAllTaskIds ?? []),
                'deprecated_task_ids' => join(',', $deprecatedTaskIds ?? []),
                'deprecated_entity_ids' => join(',', $deprecatedAllEntityIds ?? [])
            ];
            unset($deliveryTaskIds, $deliveryAllTaskIds, $deprecatedTaskIds, $deprecatedAllEntityIds);
        }
        $updatedNodeList = [];
        foreach ($originPlanNodeUpdateList as $updateData) {
            $updateData['status'] = OriginalPlanNodeModel::STATUS_EXEC;
            try {
                model(OriginalPlanNodeModel::class)->modifyItem($updateData);
                //更新节点详情表
                $this->updateOriginalNodeDetail($updateData['delivery_task_ids'], $updateData['deprecated_task_ids']);

                $updatedNodeList[] = $updateData['id'];
            } catch (\Throwable $error) {
                trace(format_exception_info($error->getMessage()), 'ERR');
            }
        }
        $hookData = [
            'type' => 'update_origin_node_task_list',
            'data' => [
                'node_id' => $updatedNodeList,
            ]
        ];
        Hook::listen('origin_node_reaching', $hookData);
        return $originPlanNodeUpdateList;
    }


    /**
     * 更新---原计划节点详细表
     * @param string $deliveryTaskIds 交付任务IDs
     * @param string $deprecatedTaskIds 取消的任务IDs
     * @return void
     */
    public function updateOriginalNodeDetail(string $deliveryTaskIds, string $deprecatedTaskIds)
    {
        $nodeDetailMod = model(OriginalPlanNodeDetailModel::class);
        //进行中的任务--更新成 已交付状态
        if (!empty($deliveryTaskIds)) {
            $condition = [
                'task_id' => ['in', explode(',', $deliveryTaskIds)],
                //进行中的任务
                'delivery_status' => OriginalPlanNodeDetailModel::DELIVERY_STATUS_DEFAULT,
            ];

            //有记录--再更新
            $hasOne = $nodeDetailMod->where($condition)->field('id')->find();
            if (!empty($hasOne)) {
                //更新成已交付状态
                $nodeDetailMod->where($condition)->save(['delivery_status' => OriginalPlanNodeDetailModel::DELIVERY_STATUS_DELIVERED]);
            }
        }
    }


    /**
     * 查询实体信息
     * @param $taskIds
     * @return array|mixed|string
     */
    public function getEntityTaskInfo($taskIds)
    {
        if (empty($taskIds)) {
            return [];
        }
        $entityTaskInfo = model(TaskModel::class)
            ->field([
                'entity.id as entity_id',
                'task.id as task_id',
                "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status')) as task_status",
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_', 'status'))) as entity_status",
            ])
            ->join('entity on entity.id = task.entity_id', 'LEFT')
            ->join('module on module.id = entity.module_id', 'LEFT')
            ->where(['task.id' => ['IN', $taskIds]])
            ->select();
        if (empty($entityTaskInfo)) {
            return [];
        }
        return $entityTaskInfo;
    }


    /**
     * 获取原计划排期节点---日报列表
     * @param $param
     * @return array
     */
    public function getDailyList($param, $page)
    {
        if (($param['end_time'] - $param['start_time']) > 7 * 24 * 3600) {
            throw new LogicException('The time range cannot exceed one week', ErrorCode::TIME_RANGE_UNREASONABLE);
        }
        //环节ID
        $stepCategoryIds = explode(',', $param['step_category_id']);

        $where = [];
        //查询条件
        $where['opn.node_type'] = OriginalPlanNodeModel::NODE_TYPE_DELIVERY;
        $where['opn.step_category_id'] = ['in', $stepCategoryIds];
        $where['opn.project_id'] = $param['project_id'];
        $where['opn.delivery_time'] = ['BETWEEN', [$param['start_time'], $param['end_time']]];

        //查询节点列表
        $list = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join("entity e on e.id = opn.episode_id", 'LEFT')
            ->where($where)
            ->field('opn.id,opn.name as task_name,opn.type,opn.link_id,opn.all_task_ids,opn.delivery_task_ids,opn.step_category_id,e.name as initial_episode_code')
            ->limit(C("database.database_max_select_rows"))
            ->select();

        //整理节点信息--组装任务ID
        $taskIdsList = $originalList = [];
        foreach ($list as $val) {
            $allTaskIdList = explode(',', $val['all_task_ids']);
            $deliveryTaskIdList = explode(',', $val['delivery_task_ids']);
            //得到---异常任务IDs = 所有任务ids-交付任务ids
            $tempList = array_diff($allTaskIdList, $deliveryTaskIdList);
            $taskIdsList = array_merge($taskIdsList, $tempList);

            //组装节点--信息
            $originalList = $this->clearOriginalData($tempList, $val, $originalList);
        }
        $taskIdsList = array_values(array_unique($taskIdsList));
        //按分页大小切割--- $page[0]开始  $page[1]=size
        $taskIdsChunk = array_chunk($taskIdsList, $page[1]);
        //存在数据
        $num = $page[0] - 1;
        $returnList = [];
        if (isset($taskIdsChunk[$num])) {
            $returnList = $this->clearDailyData($taskIdsChunk[$num], $originalList, $param['start_time']);
        }
        return [
            'list' => $returnList,
            'is_next_page' => isset($taskIdsChunk[$page[0]])
        ];
    }

    /**
     * 组装节点--信息
     * @param array $taskIds 任务IDs
     * @param array $info 节点信息
     * @param array $originalList 返回数组
     * @return array
     */
    private function clearOriginalData(array $taskIds, array $info, array $originalList)
    {
        foreach ($taskIds as $val) {
            //有延期单--记录--就不覆盖
            if (isset($originalList[$val]['type'])
                && in_array($originalList[$val]['type'], [OriginalPlanNodeModel::TYPE_DELAY_ORDER, OriginalPlanNodeModel::TYPE_DELAY_EARLY])) {
                continue;
            }
            $originalList[$val]['type'] = $info['type'] ?? 0;
            $originalList[$val]['link_id'] = $info['link_id'] ?? 0;
            $originalList[$val]['step_category_id'] = $info['step_category_id'] ?? 0;
            $originalList[$val]['initial_episode_code'] = $info['initial_episode_code'] ?? '';
            $originalList[$val]['task_name'] = $info['task_name'] ?? '';
        }
        return $originalList;
    }


    /**
     * 整理日报数据--异常任务
     * @param array $taskIds 异常任务ids
     * @param array $info 要组装的信息
     * @return array
     */
    private function clearDailyData(array $taskIds, array $info, int $startTime = 0)
    {
        if (empty($taskIds)) {
            return [];
        }

        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        //判断--查询是否是当天
        $todayTime = strtotime(date('Y-m-d'));
        $statusArray = [$closedStatus['id']];
        $cacheNum = C('database.default_query_cache_time');
        if ($startTime >= $todayTime) {
            $statusArray = [$closedStatus['id'], $doneStatus['id']];
            $cacheNum = false;
        }

        //条件
        $condition = [];
        $condition['t.id'] = ['IN', $taskIds];
        $condition["t.json->>'$.task_status'"] = ['NOT IN', $statusArray];

        //工序名
        $stepList = StepService::getInstance()->getList([], 'id,name');
        $stepList = array_column($stepList, null, 'id');
        //查询任务表task
        $list = model(TaskModel::class)->alias('t')
            ->join("entity e on e.id = t.entity_id", 'LEFT')
            ->field("t.id,t.entity_id,t.step_id,t.end_time,t.step_type,t.delivery_time,t.plan_delivery_time,t.plan_end_time,t.source_entity_id,e.showings_number,e.name,json_unquote(json_extract(t.json, '$.task_status')) as task_status")
            ->where($condition)
            ->cache($cacheNum)
            ->select();

        if (empty($list)) {
            return [];
        }

        foreach ($list as &$val) {
            $val['type'] = $info[$val['id']]['type'] ?? 0;
            $val['link_id'] = $info[$val['id']]['link_id'] ?? 0;
            $val['step_category_id'] = $info[$val['id']]['step_category_id'] ?? 0;
            $val['step_name'] = $stepList[$val['step_id']]['name'] ?? '';

            $val['initial_episode_code'] = $info[$val['id']]['initial_episode_code'] ?? '';
            $val['task_name'] = $info[$val['id']]['task_name'] ?? '';
        }

        return $list;
    }


    /**
     * 获取日报统计
     * @param int $projectId 项目ID
     * @param int $startTime 开始时间
     * @param int $endTime 结束时间
     * @param array $stepCategoryIds 环节ID
     * @param int $targetStepCategoryId 目标环节ID
     * @return array
     */
    public function getDailyStat(int $projectId, int $startTime, int $endTime, array $stepCategoryIds, int $targetStepCategoryId = 0)
    {
        if (($endTime - $startTime) > 7 * 24 * 3600) {
            throw new LogicException('The time range cannot exceed one week', ErrorCode::TIME_RANGE_UNREASONABLE);
        }

        //环节ID
        $where = [];
        //上游应交付异常任务
        if ($targetStepCategoryId) {
            $where['_string'] = "find_in_set({$targetStepCategoryId}, target_step_category_ids)";
            //过滤自身环节
            $where['step_category_id'] = ['not in', $stepCategoryIds];
        } else {
            $where['step_category_id'] = ['in', $stepCategoryIds];
        }

        // 查询必要条件
        $where['node_type'] = OriginalPlanNodeModel::NODE_TYPE_DELIVERY;
        $where['project_id'] = $projectId;
        $where['delivery_time'] = ['BETWEEN', [$startTime, $endTime]];

        // 计划交付时间是本周的节点 && （类型是正常的原计划节点 || （类型是延期节点 && 延期节点创建时间非本周） ）= 本周正常的原计划任务交付数量
        $createTime = strtotime(date('Y-m-d', $startTime));
        $where['_complex'] = [
            'type' => ['IN', [OriginalPlanNodeModel::TYPE_STANDARD_PLAN, OriginalPlanNodeModel::TYPE_INNER_PLAN, OriginalPlanNodeModel::TYPE_STANDARD_NOTE, OriginalPlanNodeModel::TYPE_NOTE]],
            '_logic' => 'or',
            '_complex' => [
                'type' => ['IN', [OriginalPlanNodeModel::TYPE_DELAY_ORDER, OriginalPlanNodeModel::TYPE_WORK_ORDER]],
                'created' => ['LT', (int)$createTime]
            ]
        ];

        //查询节点列表
        $list = model(OriginalPlanNodeModel::class)
            ->where($where)
            ->field('all_task_ids,delivery_task_ids,step_category_id')
            ->select();

        $deliveryIdsStr = $allTaskIdStr = '';
        $originalList = [];
        foreach ($list as $val) {
            $allTaskIdStr .= ',' . $val['all_task_ids'];
            $deliveryIdsStr .= ',' . $val['delivery_task_ids'];

            //暂存节点信息（环节ID）
            $originalList = $this->clearOriginalData(explode(',', $val['all_task_ids']), $val, $originalList);
        }

        //判断--是否是查询当天
        $today = 0;
        $todayTime = strtotime(date('Y-m-d'));
        if ($startTime >= $todayTime) {
            $today = 1;
            $deliveryIdsStr = $allTaskIdStr;
        }

        $rt = [];
        $rt['all_task_num'] = $this->getTaskStat($allTaskIdStr, 0, $originalList);
        $rt['delivery_num'] = $this->getTaskStat($deliveryIdsStr, $today, $originalList);

        return $rt;
    }

    /**
     * 获取延期记录列表
     * @param $param
     * @return array
     */
    public function getDailyDelayList($param, $page)
    {
        if (($param['end_time'] - $param['start_time']) > 7 * 24 * 3600) {
            throw new LogicException('The time range cannot exceed one week', ErrorCode::TIME_RANGE_UNREASONABLE);
        }

        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $where = [];
        //查询条件  ---  查询--延期单创建时间--在这个范围---并且类型为：申请延期 --- 工序任务
        $where['dot.project_id'] = $param['project_id'];
        $where['dot.created'] = ['BETWEEN', [$param['start_time'], $param['end_time']]];
        $where["t.json->>'$.task_status'"] = ['neq', $closedStatus['id']];

        //查询节点列表
        $list = model(DelayOrderTaskModel::class)->alias('dot')
            ->join("task t on t.id = dot.task_id", 'LEFT')
            ->where($where)
            ->field("dot.id,dot.confirm_status,dot.type,dot.task_id,dot.delay_order_id as link_id,JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.task_status')) as task_status")
            ->page($page[0], $page[1])
            ->select();

        return $list;
    }


    /**
     * 获取任务统计
     * @param string $allTaskIdStr 任务ID字符串
     * @param int $today 是否统计今天
     * @param array $originalList 暂存的节点信息
     * @return array|int
     */
    private function getTaskStat(string $allTaskIdStr, int $today = 0, array $originalList = [])
    {
        $allTaskIds = array_unique(explode(',', $allTaskIdStr));
        if (empty($allTaskIds)) {
            return 0;
        }
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
        //条件
        $condition = [];
        if ($today) {
            $condition["json->>'$.task_status'"] = $doneStatus['id'];
            $cacheNum = false;
        } else {
            $cacheNum = C('database.default_query_cache_time');
            $condition["json->>'$.task_status'"] = ['neq', $closedStatus['id']];
        }

        $tempList = [];
        foreach (array_chunk($allTaskIds, 200) as $taskIds) {

            $condition['id'] = ['IN', $taskIds];

            $list = model(TaskModel::class)->field('id,step_type,step_category_id')
                ->where($condition)
                ->cache($cacheNum)
                ->select();
            foreach ($list as &$val) {
                //标准任务--任务表上没有环节--引用节点上的环节
                if (empty($val['step_category_id'])) {
                    $val['step_category_id'] = $originalList[$val['id']]['step_category_id'] ?? 0;
                }
            }
            $tempList = array_merge($tempList, $list);
        }
        return $tempList;

    }

    /**
     * 获取实体--任务列表
     * @param $param
     * @return array
     */
    public function getEntityTaskList($param, $page)
    {
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $where = [];
        //查询条件
        $sourceEntityIds = explode(',', $param['source_entity_id']);

        $where['project_id'] = $param['project_id'];
        $where['source_entity_id'] = ['in', $sourceEntityIds];
        $where['step_type'] = TaskModel::STEP_TYPE_STEP;
        $where["json->>'$.task_status'"] = ['neq', $closedStatus['id']];
        //过滤子任务
//        $where["_string"] = "JSON_UNQUOTE(JSON_EXTRACT(json,'$.is_dismantle_task')) is null";

        //查询节点列表
        return model(TaskModel::class)
            ->where($where)
            ->field("id,source_entity_id,end_time,entity_id,plan_delivery_time,delivery_time,step_category_id,JSON_UNQUOTE(JSON_EXTRACT(json, '$.task_status')) as task_status")
            ->page($page[0], $page[1])
            ->select();

    }

    /**
     * 日报--通过任务ID查询工单记录
     * @param $param
     * @return array
     */
    public function getWorkOrderListByTaskId($param)
    {
        //查询条件
        $taskIds = explode(',', $param['task_ids']);

        //获取最大ID
        $maxWorkerOrderId = model(TaskWorkOrderModel::class)
            ->where(['task_id' => ['IN', $taskIds]])
            ->field('max(id) as id')
            ->group('task_id')
            ->cache(C('database.default_query_cache_time'))
            ->column('id');

        if (empty($maxWorkerOrderId)) {
            return [];
        }

        //工单
        return model(TaskWorkOrderModel::class)
            ->where(['id' => ['IN', $maxWorkerOrderId]])
            ->field('id as link_id, task_id, status, created')
            ->select();
    }


    /**
     * 日报--通过任务ID查询延期记录
     * @param $param
     * @return array
     */
    public function getDelayListByTaskId($param)
    {
        //查询条件
        $taskIds = explode(',', $param['task_ids']);

        $delayOrderType = [
            DelayOrderTaskModel::TYPE_REQUEST_DELAY,
            DelayOrderTaskModel::TYPE_TODAY_DONE,
            DelayOrderTaskModel::TYPE_DEMAND_ORDER,
            DelayOrderTaskModel::TYPE_WORK_ORDER,
        ];
        //获取最大ID
        return model(DelayOrderTaskModel::class)
            ->where(['task_id' => ['IN', $taskIds]])
            ->where(['type' => ['IN', $delayOrderType]])
            ->field("id, delay_order_id as link_id, type, created, task_id, plan_delivery_time, confirm_status, delay_time, link_id as demand_order_id")
            ->cache(C('database.query_cache_one_minute'))
            ->select();
    }

    /**
     * 获取需求单列表 --- 是否标准新增
     * @param $param
     * @return array
     */
    public function getDemandOrderDelayList($param)
    {
        //查询条件
        $reviewFeedbackId = explode(',', $param['demand_order_id']);

        //列表
        return model(ReviewFeedbackModel::class)
            ->where(['id' => ['IN', $reviewFeedbackId]])
            ->field("id, standard_append")
            ->select();
    }

    /**
     * 根据延期记录id，获取延期确认列表
     * @param $param
     * @return array
     */
    public function getDelayOrderTaskStepCategoryConfirm($param)
    {
        //查询条件
        $delayOrderIds = explode(',', $param['delay_order_task_id']);

        //查询--延期单任务环节确认情况
        return model(DelayOrderTaskStepCategoryConfirmModel::class)->alias('cc')
            ->join("delay_order delay on delay.id = cc.delay_order_id")
            ->join("delay_order_task dot on dot.id = cc.delay_order_task_id")
            ->where(['cc.delay_order_task_id' => ['in', $delayOrderIds]])
            ->field("dot.task_id,dot.plan_delivery_time,dot.step_category_confirmed,dot.manager_confirmed,cc.delay_to,cc.delay_order_task_id,cc.step_category_id,cc.is_pass,cc.is_active,cc.is_confirmed,cc.is_self_step_category,delay.confirm_status,delay.created")
            ->cache(C('database.query_cache_one_minute'))
            ->select();
    }

    /**
     * 根据工单id，获取问题环节确认列表
     * @param $param
     * @return array
     */
    public function getTaskWorkOrderProblemStepCategory($param)
    {
        //查询条件
        $taskWorkOrderIds = explode(',', $param['task_work_order_id']);

        //查询--工单问题环节D
        return model(TaskWorkOrderProblemStepCategoryModel::class)
            ->where(['task_work_order_id' => ['in', $taskWorkOrderIds]])
            ->field("task_work_order_id, step_category_id, is_confirm")
            ->select();
    }

    /**
     * 获取标准主任务信息---通过子任务
     * @param $param
     * @return array
     */
    public function getSourceTaskList($param)
    {
        //查询条件
        $taskIds = explode(',', $param['task_ids']);
        //查询主任务列表 --- 模板待办类型 source_task_id = 主任务
        $sourceTaskList = model(TaskRelationModel::class)
            ->where(['target_task_id' => ['in', $taskIds], 'relation_type' => TaskRelationModel::RELATION_TYPE_TEMPLATE_TODO])
            ->field('source_task_id,target_task_id')
            ->cache(C('database.default_query_cache_time'))
            ->select();
        if (empty($sourceTaskList)) {
            return [];
        }
        //主任务IDs
        $sourceTaskId = array_column($sourceTaskList, 'source_task_id');
        //以主任务ID为key
        $targetTaskList = array_column($sourceTaskList, null, 'source_task_id');

        //查询主任务信息
        $taskList = model(TaskModel::class)
            ->field("id,name,delivery_time,plan_delivery_time,plan_end_time,JSON_UNQUOTE(JSON_EXTRACT(json, '$.task_status')) as task_status")
            ->where(['id' => ['IN', $sourceTaskId]])
            ->select();

        //返回--主子任务ID
        foreach ($taskList as &$val) {
            $val['sub_task_id'] = $targetTaskList[$val['id']]['target_task_id'] ?? 0;
        }

        return $taskList;
    }

    /**
     * 日报上报表--上报（备注）
     * @param $param
     * @return array|bool|void
     */
    public function planNodeReport($param)
    {
        //组装数据
        $data = [
            'project_id' => $param['project_id'],
            'report_date' => $param['report_date'],
            'step_category_id' => $param['step_category_id'],
        ];
        //判断是否重复提交
        $reportModel = model(ProjectNodeReportModel::class);
        $hasOne = $reportModel->field('id')->where($data)->find();
        try {
            //备注信息
            $json = $param['json'] ?? [];
            $data['json'] = json_encode($json);
            //更新或修改
            if ($hasOne) {
                $data['id'] = $hasOne['id'];
                $rt = $reportModel->modifyItem($data);
            } else {
                $rt = $reportModel->addItem($data);
            }
            return $rt;
        } catch (\Exception $e) {
            trace('日报备注失败' . PHP_EOL . format_exception_info($e), 'ERR');
        }
    }

    /**
     * 日报上报表--列表
     * @param $param
     * @return array
     */
    public function planNodeReportList($param)
    {
        //查询条件
        $where = [
            'project_id' => $param['project_id'],
            'report_date' => $param['report_date'],
        ];
        if (!empty($param['step_category_id'])) {
            $where['step_category_id'] = $param['step_category_id'];
        }
        //返回--列表
        return model(ProjectNodeReportModel::class)->where($where)->select();
    }

    /**
     * 生成异常任务必要查询条件的
     * @return array
     */
    protected function generateAbnormalTaskMustFilter($projectId, $stepCategoryIds)
    {
        return [
            //查询交付节点
            'opn.node_type' => OriginalPlanNodeDetailModel::NODE_TYPE_DELIVERY,
            //查询状态：未交付 和 进行中的
            'opn.delivery_status' => ['in', [OriginalPlanNodeDetailModel::DELIVERY_STATUS_UNDELIVERED, OriginalPlanNodeDetailModel::DELIVERY_STATUS_DEFAULT]],
            'opn.step_category_id' => ['in', $stepCategoryIds],
            'opn.project_id' => $projectId
        ];
    }

    /**
     * 生成异常标准任务任务必要查询条件的
     * @param $projectId
     * @param $stepTypes
     * @return array
     */
    protected function generateAbnormalStandardTaskMustFilter($projectId, $stepTypes)
    {
        return [
            //查询交付节点
            'opn.node_type' => OriginalPlanNodeDetailModel::NODE_TYPE_DELIVERY,
            //查询状态：未交付 和 进行中的
            'opn.delivery_status' => ['in', [OriginalPlanNodeDetailModel::DELIVERY_STATUS_UNDELIVERED, OriginalPlanNodeDetailModel::DELIVERY_STATUS_DEFAULT]],
            'opn.step_type' => ['in', $stepTypes],
            'opn.project_id' => $projectId
        ];
    }

    /**
     * 获取指定过滤条件的异常任务数量
     * @param $condition
     * @param $statusCondition
     * @return int|null
     */
    protected function getAbnormalTaskCount($condition, $statusCondition)
    {
        // 降低统计损耗，可以先判断是否存在一条任务数据
        $existAbnormalTask = model(OriginalPlanNodeDetailModel::class)->field('id')->alias('opn')->where($condition)->find();

        $count = 0;
        if (!empty($existAbnormalTask)) {
            // 以 OriginalPlanNodeDetail 作为主表，按entity_id去重统计数量
            $condition["t.json->>'$.task_status'"] = $statusCondition;

            $taskIds = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                ->join("task t on t.id = opn.task_id", 'LEFT')
                ->field('distinct opn.task_id')
                ->where($condition)
                ->limit(100)
                ->select();

            if (!empty($taskIds)) {
                $count = count($taskIds);
            }
        }

        return $count;
    }

    /**
     * 获取指定过滤条件的异常实体数量
     * @param $condition
     * @param $statusCondition
     * @return int|null
     */
    protected function getAbnormalEntityCount($condition, $statusCondition)
    {
        // 降低统计损耗，可以先判断是否存在一条任务数据
        $existAbnormalTask = model(OriginalPlanNodeDetailModel::class)->field('id')->alias('opn')->where($condition)->find();

        $count = 0;
        if (!empty($existAbnormalTask)) {
            // 以 OriginalPlanNodeDetail 作为主表，按entity_id去重统计数量
            $condition["t.json->>'$.task_status'"] = $statusCondition;

            $entityIds = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                ->join("task t on t.id = opn.task_id", 'LEFT')
                ->field('distinct opn.entity_id')
                ->where($condition)
                ->group('opn.entity_id')
                ->limit(100)
                ->select();

            if (!empty($entityIds)) {
                $count = count($entityIds);
            }
        }

        return $count;
    }

    /**
     * 按分类统计异常任务数量最多统计按任务去重
     * @param string $stepType
     * @param int $projectId
     * @param array $timeRange
     * @return int[]|null[]
     */
    public function countAbnormalTask(string $stepType, int $projectId, array $timeRange)
    {

        // standard_review,standard_meeting 走step_type条件查询
        $condition = $this->generateAbnormalStandardTaskMustFilter($projectId, [$stepType]);

        // 分为两次统计
        // 获取已经关闭的任务状态列表，历史数据要用
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        // 获取已经完成的任务装列表，今天数据要用，还来不及生成节点缓存数据需要实时判断数据
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();

        // 拆分时间范围，拆成历史和今天时间范围
        $timeRange = split_time_range($timeRange);

        // 1.历史节点数据判断原计划节点任务未交付，排除任务已废除的状态
        $historyCount = 0;
        if (!empty($timeRange['history'])) {
            // 统计历史任务异常数据
            $condition['opn.original_delivery_time'] = ['between', $timeRange['history']];
            $statusCondition = ['neq', $closedStatus['id']];
            $historyCount = $this->getAbnormalTaskCount($condition, $statusCondition);
        }

        // 2.今天节点数据判断原计划节点任务未交付，排除任务已废除和已完成状态
        $todayCount = 0;
        if (!empty($timeRange['today'])) {
            // 统计今天任务异常数据
            $condition['opn.original_delivery_time'] = ['between', $timeRange['today']];
            $statusCondition = ['not in', [$closedStatus['id'], $doneStatus['id']]];
            $todayCount = $this->getAbnormalTaskCount($condition, $statusCondition);
        }

        return ['count' => ($historyCount + $todayCount)];

    }

    /**
     * 按分类统计异常任务数量最多统计按实体去重 100次，大于99次显示 99+
     * @param string $entityModuleCode
     * @param int $projectId
     * @param array $timeRange
     * @return array
     */
    public function countAbnormalEntity(string $entityModuleCode, int $projectId, array $timeRange)
    {
        //查询条件
        // 获取当前模块所属的环节ids
        $stepCategoryIds = StepCategoryService::getInstance()->getStepCategoryListByEntity($entityModuleCode);
        // 必须条件
        $condition = $this->generateAbnormalTaskMustFilter($projectId, $stepCategoryIds);

        // 分为两次统计

        // 获取已经关闭的任务状态列表，历史数据要用
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        // 获取已经完成的任务装列表，今天数据要用，还来不及生成节点缓存数据需要实时判断数据
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();

        // 拆分时间范围，拆成历史和今天时间范围
        $timeRange = split_time_range($timeRange);

        // 1.历史节点数据判断原计划节点任务未交付，排除任务已废除的状态
        $historyCount = 0;
        if (!empty($timeRange['history'])) {
            // 统计历史任务异常数据
            $condition['opn.original_delivery_time'] = ['between', $timeRange['history']];
            $statusCondition = ['neq', $closedStatus['id']];
            $historyCount = $this->getAbnormalEntityCount($condition, $statusCondition);
        }

        // 2.今天节点数据判断原计划节点任务未交付，排除任务已废除和已完成状态
        $todayCount = 0;
        if (!empty($timeRange['today'])) {
            // 统计今天任务异常数据
            $condition['opn.original_delivery_time'] = ['between', $timeRange['today']];
            $statusCondition = ['not in', [$closedStatus['id'], $doneStatus['id']]];
            $todayCount = $this->getAbnormalEntityCount($condition, $statusCondition);
        }

        return ['count' => ($historyCount + $todayCount)];
    }

    /**
     * 获取今天时间范围内存在异常的集数列表，补偿StarRocks获取的数据统计
     * @param string $entityModuleCode
     * @param int $projectId
     * @return array
     */
    public function getTodayExistAbnormalEpisodeList(string $entityModuleCode, int $projectId)
    {
        if (in_array($entityModuleCode, ['episode', 'shot', 'asset'])) {
            // 按实体分类做数据统计

            $stepCategoryIds = StepCategoryService::getInstance()->getStepCategoryListByEntity($entityModuleCode);

            // 必须条件
            $condition = $this->generateAbnormalTaskMustFilter($projectId, $stepCategoryIds);
            $countType = 'entity';
        } else {
            // 按任务类型做数据统计
            $condition = $this->generateAbnormalStandardTaskMustFilter($projectId, [$entityModuleCode]);
            $countType = 'task';
        }


        // original_delivery_time 在今天范围内还未完成的，要连表task任务表实时判断
        $condition['opn.original_delivery_time'] = ['between', [get_today_start_time(), get_today_end_time()]];

        // 降低统计损耗，可以先判断是否存在一条任务数据
        $existAbnormalTask = model(OriginalPlanNodeDetailModel::class)->field('id')->alias('opn')->where($condition)->find();

        // [{"name":"EP001", "count": 2}]
        $episodeList = [];
        if (!empty($existAbnormalTask)) {
            // 获取已经关闭的任务状态列表，历史数据要用
            $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
            // 获取已经完成的任务装列表，今天数据要用，还来不及生成节点缓存数据需要实时判断数据
            $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
            $condition["t.json->>'$.task_status'"] = ['not in', [$closedStatus['id'], $doneStatus['id']]];

            if ($countType == 'entity') {
                $episodeList = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                    ->join("task t on t.id = opn.task_id", 'LEFT')
                    ->field('opn.initial_episode_code as name,count(distinct opn.entity_id) as count')
                    ->where($condition)
                    ->group('opn.initial_episode_code')
                    ->select();
            } else {
                $episodeList = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                    ->join("task t on t.id = opn.task_id", 'LEFT')
                    ->field('opn.initial_episode_code as name,count(distinct opn.task_id) as count')
                    ->where($condition)
                    ->group('opn.initial_episode_code')
                    ->select();
            }


            if (empty($episodeList)) {
                return [];
            }
        }

        return $episodeList;
    }

    /**
     * 获取指定集数异常实体或者任务列表
     * @param string $entityModuleCode
     * @param int $projectId
     * @param string $episodeCode
     * @param array $timeRange
     * @param array $page
     * @return array
     */
    public function getEpisodeAbnormalEntityOrTaskList(string $entityModuleCode, int $projectId, string $episodeCode, array $timeRange, array $page = [1, 100])
    {
        // 限制时间范围只能最大取一年
        check_time_range_max($timeRange, 31556952);

        $stepCategoryIds = [];
        if (in_array($entityModuleCode, ['episode', 'shot', 'asset'])) {
            // 按实体分类做数据统计
            // 1. 查找各环节的二级分类ids
            // 找绑定实体的分类的子分类
            // 剧本：剧本环节
            // 镜头：分镜+镜头实体
            // 资产：概念+资产+关卡实体
            $stepCategoryIds = StepCategoryService::getInstance()->getStepCategoryListByEntity($entityModuleCode);

            // 必须条件
            $countType = 'entity';
        } else {
            // 按任务类型做数据统计
            $countType = 'task';
        }

        // 原计划节点关联任务表 original_plan_node_detail

        // 2. 组装查询条件
        // 必须条件
        // 拆分时间范围，拆成历史和今天时间范围
        $timeRange = split_time_range($timeRange);

        // 获取已经关闭的任务状态列表，历史数据要用
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        // 获取已经完成的任务装列表，今天数据要用，还来不及生成节点缓存数据需要实时判断数据
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();


        $historyCondition = [];
        if (!empty($timeRange['history'])) {
            // 组装历史数据查询条件
            if ($countType == 'entity') {
                $historyCondition = $this->generateAbnormalTaskMustFilter($projectId, $stepCategoryIds);
            } else {
                $historyCondition = $this->generateAbnormalStandardTaskMustFilter($projectId, [$entityModuleCode]);
            }

            $historyCondition['opn.initial_episode_code'] = $episodeCode;
            $historyCondition['opn.original_delivery_time'] = ['between', $timeRange['history']];
            $historyCondition["t.json->>'$.task_status'"] = ['neq', $closedStatus['id']];
        }

        $todayCondition = [];
        if (!empty($timeRange['today'])) {
            // 组装今天数据查询条件
            if ($countType == 'entity') {
                $todayCondition = $this->generateAbnormalTaskMustFilter($projectId, $stepCategoryIds);
            } else {
                $todayCondition = $this->generateAbnormalStandardTaskMustFilter($projectId, [$entityModuleCode]);
            }
            $todayCondition['opn.initial_episode_code'] = $episodeCode;
            $todayCondition['opn.original_delivery_time'] = ['between', $timeRange['today']];
            $todayCondition["t.json->>'$.task_status'"] = ['not in', [$closedStatus['id'], $doneStatus['id']]];
        }

        // 合并查询条件
        $condition = [];
        if (!empty($historyCondition) && !empty($todayCondition)) {
            $condition[] = $historyCondition;
            $condition[] = $todayCondition;
            $condition['_logic'] = 'or';
        } else {
            if (!empty($historyCondition)) {
                $condition = $historyCondition;
            } else {
                $condition = $todayCondition;
            }
        }

        $resData = [];
        if (!empty($condition)) {
            // 3. 根据分类二级ids 查找，按entity_id分页得到 entity_ids
            if ($page[1] > 100) {
                // 单次分页数不能超过100
                $page[1] = 100;
            }
            if ($countType == 'entity') {
                $entityIdsData = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                    ->join("task t on t.id = opn.task_id", 'LEFT')
                    ->join("entity e on e.id = opn.entity_id", 'LEFT')
                    ->field('distinct opn.entity_id as id,opn.entity_module_id,opn.initial_episode_code,opn.showings_number,e.json,e.is_append,e.name,t.source_entity_id')
                    ->where($condition)
                    ->page($page[0], $page[1])
                    ->select();

                //整理返回--异常任务数据
                return $this->clearUpAbnormalTaskList($entityIdsData);
            } else {
                $taskData = model(OriginalPlanNodeDetailModel::class)->alias('opn')
                    ->join("task t on t.id = opn.task_id", 'LEFT')
                    ->field('
                    distinct opn.task_id as id,
                    t.name as name,
                    opn.initial_episode_code 
                    ')
                    ->where($condition)
                    ->page($page[0], $page[1])
                    ->group('opn.task_id')
                    ->select();
                if (empty($taskData)) {
                    return [];
                }

                return $taskData;
            }
        }

        return $resData;
    }

    /**
     * 根据异常实体id获取下面异常任务列表
     * @param string $entityModuleCode
     * @param int $projectId
     * @param array $entityIds
     * @return array
     */
    public function getEpisodeAbnormalTaskMapByEntity(string $entityModuleCode, int $projectId, array $entityIds)
    {
        // 返回数据
        $entityTaskGroupByStepCategory = [];

        // 1. 查找各环节的二级分类ids
        $stepCategoryIds = StepCategoryService::getInstance()->getStepCategoryListByEntity($entityModuleCode);
        // 获取已经关闭的任务状态列表，历史数据要用
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        // 2. 按entity_ids in条件把任务取出来
        $entityTaskData = model(OriginalPlanNodeDetailModel::class)->alias('opn')
            ->field('
            opn.entity_id as e_id,
            opn.step_category_id as s_id,
            opn.delivery_status as status,
            opn.task_id as t_id,
            opn.close_link_type as c_type,
            opn.close_link_id as cl_id,
            opn.original_delivery_time as d_time,
            opn.created
            ')
            ->join("task t on t.id = opn.task_id", 'LEFT')
            ->where([
                //查询交付节点
                'opn.node_type' => OriginalPlanNodeDetailModel::NODE_TYPE_DELIVERY,
                'opn.step_category_id' => ['in', $stepCategoryIds],
                'opn.project_id' => $projectId,
                'opn.entity_id' => ['in', $entityIds],
                "t.json->>'$.task_status'" => ['neq', $closedStatus['id']]
            ])
            ->select();

        if (!empty($entityTaskData)) {
            // 3. 按entity分组
            foreach ($entityTaskData as $entityTaskItem) {
                $stepCategoryItem = [
                    'id' => $entityTaskItem['t_id'], // task_id
                    'status' => $entityTaskItem['status'], // delivery_status
                    'c_type' => $entityTaskItem['c_type'], // close_link_type
                    'cl_id' => $entityTaskItem['cl_id'], // close_link_id
                    'd_time' => $entityTaskItem['d_time'], // original_delivery_time
                    'created' => $entityTaskItem['created'] // close_link_id
                ];

                if (array_key_exists($entityTaskItem['e_id'], $entityTaskGroupByStepCategory)) {
                    if (array_key_exists($entityTaskItem['s_id'], $entityTaskGroupByStepCategory[$entityTaskItem['e_id']])) {
                        $entityTaskGroupByStepCategory[$entityTaskItem['e_id']][$entityTaskItem['s_id']][] = $stepCategoryItem;
                    } else {
                        $entityTaskGroupByStepCategory[$entityTaskItem['e_id']][$entityTaskItem['s_id']] = [
                            $stepCategoryItem
                        ];
                    }
                } else {
                    $entityTaskGroupByStepCategory[$entityTaskItem['e_id']] = [
                        $entityTaskItem['s_id'] => [
                            $stepCategoryItem
                        ]
                    ];
                }
            }

            unset($entityTaskData);
        }

        return $entityTaskGroupByStepCategory;
    }

    /**
     * 根据异常任务id获取下面异常任务列表
     * @param string $stepType
     * @param int $projectId
     * @param array $taskIds
     * @return array
     */
    public function getEpisodeAbnormalTaskMapByTask(string $stepType, int $projectId, array $taskIds)
    {
        // 返回数据
        $listGroupByTask = [];

        // 获取已经关闭的任务状态列表，历史数据要用
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        // 2. 按task_ids in条件把任务取出来
        $taskData = model(OriginalPlanNodeDetailModel::class)->alias('opn')
            ->field('
            opn.entity_id as e_id,
            opn.step_category_id as s_id,
            opn.delivery_status as status,
            opn.task_id as t_id,
            opn.close_link_type as c_type,
            opn.close_link_id as cl_id,
            opn.original_delivery_time as d_time,
            opn.created
            ')
            ->join("task t on t.id = opn.task_id", 'LEFT')
            ->where([
                //查询交付节点
                'opn.node_type' => OriginalPlanNodeDetailModel::NODE_TYPE_DELIVERY,
                'opn.step_type' => $stepType,
                'opn.project_id' => $projectId,
                'opn.task_id' => ['in', $taskIds],
                "t.json->>'$.task_status'" => ['neq', $closedStatus['id']]
            ])
            ->select();
        if (!empty($taskData)) {
            // 3. 按task分组
            foreach ($taskData as $item) {
                $taskItem = [
                    'id' => $item['t_id'], // task_id
                    'status' => $item['status'], // delivery_status
                    'c_type' => $item['c_type'], // close_link_type
                    'cl_id' => $item['cl_id'], // close_link_id
                    'd_time' => $item['d_time'], // original_delivery_time
                    'created' => $item['created'] // close_link_id
                ];

                if (array_key_exists($item['t_id'], $listGroupByTask)) {
                    $listGroupByTask[$item['t_id']][] = $taskItem;
                } else {
                    $listGroupByTask[$item['t_id']] = [$taskItem];
                }
            }

            unset($taskData);
        }

        return $listGroupByTask;
    }

    /**
     * 获取异常任务列表
     * @param int $projectId 项目ID
     * @param array $timeRange 时间范围 [开始时间，结束时间]
     * @param array $page 分页参数 [1,100]
     */
    public function getAbnormalTaskList(int $projectId, array $timeRange, string $moduleCode, array $page = [1, 100])
    {
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        //查询条件
        $condition = [
            //查询交付节点
            'opn.node_type' => OriginalPlanNodeDetailModel::NODE_TYPE_DELIVERY,
            //查询状态：未交付 和 进行中的
            'opn.delivery_status' => ['in', [OriginalPlanNodeDetailModel::DELIVERY_STATUS_UNDELIVERED, OriginalPlanNodeDetailModel::DELIVERY_STATUS_DEFAULT]],
            //去掉已取消的任务
            "t.json->>'$.task_status'" => ['neq', $closedStatus['id']],

            'opn.project_id' => $projectId,
            'opn.original_delivery_time' => ['between', $timeRange],
        ];

        //实体类型--moduleCode
        if (!empty($moduleCode)) {
            $condition['opn.entity_module_id'] = module_by_code($moduleCode)['id'];
        }

        //查询
        $list = model(OriginalPlanNodeDetailModel::class)->alias('opn')
            ->join("task t on t.id = opn.task_id", 'LEFT')
            ->join("entity e on e.id = opn.entity_id", 'LEFT')
            ->field([
                'opn.task_id,opn.entity_id,opn.entity_module_id,opn.initial_episode_code,opn.showings_number,opn.step_category_id,opn.delivery_status,opn.close_link_type,opn.close_link_id',
                'e.json,e.is_append,e.name,t.source_entity_id',
            ])
            ->where($condition)
            ->page($page[0], $page[1])
            ->select();

        //整理返回--异常任务数据
        return $this->clearUpAbnormalTaskList($list);
    }


    /**
     * 整理返回--异常任务数据
     * @param array $list
     * @return array
     */
    private function clearUpAbnormalTaskList(array $list)
    {

        foreach ($list as &$val) {
            //实体code
            $val['module_code'] = module_by_id($val['entity_module_id'])['code'] ?? '';
            //返回等级和属性
            $json = json_decode($val['json'], true);
            $val['category'] = $json[$val['module_code'] . '_category'] ?? '';
            $val['grade'] = $json[$val['module_code'] . '_grade'] ?? '';
            //去除多余字段
            unset($val['json']);
            unset($val['entity_module_id']);
        }
        return $list;
    }


    /**
     * 获取异常任务未处理的工单列表数据
     * @param int $projectId 项目ID
     * @param array $timeRange 时间范围 [开始时间，结束时间]
     * @param array $taskIds 任务IDs
     * @return array
     */
    public function getAbnormalTaskWorkOrderList(int $projectId, array $timeRange, array $taskIds)
    {
        //查询条件
        $condition = [
            //查询状态：未处理工单
            'status' => ['IN', TaskWorkOrderModel::STATUS_NOT_CLOSED_GROUP],
            'project_id' => $projectId,
            'created' => ['between', $timeRange],
            'task_id' => ['in', $taskIds],
        ];
        //查询--列表
        return model(TaskWorkOrderModel::class)
            ->field('id as work_order_id,task_id,entity_id,step_category_id')
            ->where($condition)
            ->select();
    }

}


